/**
 * 
 */
package com.cqcis.uip.base.framework.core.service;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.osgi.framework.BundleContext;
import org.springframework.context.ApplicationContext;

import com.cqcis.uip.base.framework.FrameworkConstants;
import com.cqcis.uip.base.framework.UIPServiceException;
import com.cqcis.uip.base.framework.base.adaptor.AbstractRegisteredService;
import com.cqcis.uip.base.framework.common.Task;
import com.cqcis.uip.base.framework.core.LogService;
import com.cqcis.uip.base.framework.core.RegisteredService;
import com.cqcis.uip.base.framework.core.RegisteredServiceContext;
import com.cqcis.uip.base.framework.core.RegisteredServiceEvent;
import com.cqcis.uip.base.framework.core.RegisteredServiceListener;
import com.cqcis.uip.base.framework.core.RegisteredServiceRepository;
import com.cqcis.uip.base.framework.core.ServletContainer;
import com.cqcis.uip.base.framework.core.channel.Channel;
import com.cqcis.uip.base.framework.core.impl.ServletContainerServerFactory;
import com.cqcis.uip.base.framework.service.StreamLogOutputService;
import com.cqcis.uip.base.logadaptor.LogConfiguration;

/**
 * @author huangmy
 *
 */
public class RegisteredServiceContextImpl implements RegisteredServiceContext {
    private Channel channel; /*通道*/
    private RegisteredServiceRepository serviceRepository;/*注册服务集*/
    private LogService logService; /*日志服务*/
    private RegisteredService currentRs; /*当前服务*/    
    private Collection<RegisteredServiceListener> listeners = Collections.synchronizedList(new ArrayList<RegisteredServiceListener>());
    private Map<RegisteredService, Collection<RegisteredServiceListener>> allRSListeners;
    private StreamLogOutputService streamLogOutputService; // stream log
    private BundleContext bundleContext; // CONTEXT
    
    public RegisteredServiceContextImpl(Channel channel, 
    		            RegisteredServiceRepository serviceRepository,
    		            LogService logService, RegisteredService currentRs,
    		            Map<RegisteredService, Collection<RegisteredServiceListener>> allRSListeners,
    		            BundleContext bundleContext) {
    	this.channel = channel;
    	this.serviceRepository = serviceRepository;		
    	this.logService = logService;
    	this.currentRs = currentRs;    	
    	this.allRSListeners = allRSListeners;
    	this.bundleContext = bundleContext;
    }
    
    public RegisteredServiceContextImpl(
			Channel channel,
			RegisteredServiceRepository serviceRepository,
			LogService logService,
			RegisteredService currentRs,
			Map<RegisteredService, Collection<RegisteredServiceListener>> allRSListeners,
			StreamLogOutputService streamLogOutputService,
			BundleContext bundleContext) {
		this.channel = channel;
		this.serviceRepository = serviceRepository;
		this.logService = logService;
		this.currentRs = currentRs;
		this.allRSListeners = allRSListeners;
		this.streamLogOutputService = streamLogOutputService;
		this.bundleContext = bundleContext;
	}
    
    public BundleContext getBundleContext() {
    	return this.bundleContext;
    }
    
    public StreamLogOutputService getStreamLogOutputService() {
    	if (this.streamLogOutputService == null) {
    		throw new UnsupportedOperationException("Unsupport StreamLog Output.");
    	}
    	return this.streamLogOutputService;
    }
    
	public Channel getChannel() {		
		return channel;
	}

	public RegisteredServiceRepository getServiceRepository() {		
		return serviceRepository;
	}
		
	public void start() throws Exception {
		channel.start(this);
	}

	public void stop() throws Exception {
		channel.stop(this);		
	}
	
	public URL getResource(String name) {
		RegisteredService rs = getRegisteredService();
		if (rs != null) {
			BundleContext bc = rs.getBundleContext();
			return bc.getBundle().getResource(name);
		}
		return null;
	}

	public LogService getLogService() {		
		return logService;
	}
	
	public RegisteredService getRegisteredService() {		
		return currentRs;
	}	
	
	public void catchException(Task task, Throwable t) {
		this.currentRs.catchException(task, t);
	}
	
	public void addServiceListener(RegisteredServiceListener listener) {
		this.listeners.add(listener);
	}
	
	public void setStreamLogOutputService(StreamLogOutputService streamLogOutputService) {
		this.streamLogOutputService = streamLogOutputService;
	}
	
	public void publishRegisteredServiceEvent(RegisteredServiceEvent event) {				
		for (Entry<RegisteredService, Collection<RegisteredServiceListener>> entry : this.allRSListeners.entrySet()) {				
			RegisteredService service = entry.getKey();
			if (this.currentRs == service) {
				continue;// 不发布给本身
			}
			
            List<LogConfiguration> oldLogConfigr = logService.getLogConfiguration(Thread
					.currentThread());

			try {				
				logService.unregister(Thread.currentThread());
				logService.register(service.getLogConfiguration());
				Collection<RegisteredServiceListener> listeners = entry
						.getValue();
				for (RegisteredServiceListener l : listeners) {
					try {
						l.seviceChanged(event);
					} catch (Throwable t) {
					}
				}
			} finally {
				if (oldLogConfigr != null) {
					logService.unregister();
					for (LogConfiguration config : oldLogConfigr) {
						logService.register(config);
					}
				} else {
					logService.unregister();
				}
			}
		}
	}
	
	public Collection<RegisteredServiceListener> getServiceListeners() {
		return this.listeners;
	}
	
	public ApplicationContext getApplicationContext() {
		if (this.currentRs != null && (this.currentRs instanceof AbstractRegisteredService)) {
			return ((AbstractRegisteredService)this.currentRs).getApplicationContext();
		}
		return null;
	}
	
	public ServletContainer getServletContainer(int port) {
		try {
			return ServletContainerServerFactory.getInstance().getServer(port);
		} catch (Exception e) {
			throw new UIPServiceException(e);
		}
	}
	
	/**
	 * 根据当前context模板，获取给定RS的context实例.
	 * 需要具体说明的是： 从该实例获取的channel对象方法与真正的channel对象方法不一样，
	 * 方法将被拦截，以便增加记录如调用者，调用日志等功能。
	 * 
	 * @param currentRs
	 * @param allRSListeners
	 * @return
	 */
	public RegisteredServiceContextImpl copyFrom(final RegisteredService currentRs, Map<RegisteredService, Collection<RegisteredServiceListener>> allRSListeners) {
		if (allRSListeners == null) {
			allRSListeners = new HashMap<RegisteredService, Collection<RegisteredServiceListener>>();
		}
		
		Channel ch = new Channel() {
			public void start(RegisteredServiceContext serviceContext)
					throws Exception {	
				// do nothing
			}
			
			public void stop(RegisteredServiceContext serviceContext)
					throws Exception {
				// do nothing
			}
			
			public boolean schedule(Task task) throws Exception {
				// 记录调用				
				recordNoresult(task);
								
				return channel.schedule(task);
			}
			
			public boolean scheduleSync(Task task) throws Exception {
				// 记录调用	
				recordNoresult(task);
								
				return channel.scheduleSync(task);
			}
			
			/**
			 *  记录调用
			 * 
			 * @param task
			 */
			private void recordNoresult(Task task) {
				RegisteredService srcRs = currentRs.getRegisteredServiceContext()
				       .getServiceRepository().getRegisteredService(task.route().from());
				if (srcRs == null) {
					throw new UIPServiceException("Can't find RS for from endpoint: " 
							+ task.route().from());
				}
				
				if (task.getAttribute(FrameworkConstants.HAVA_SET_PENDRESULT) == null) {
					AbstractRegisteredService ars = (AbstractRegisteredService)srcRs;
					ars.getNoResultTasks().put(task.getToUnique(), task);
					ars.incrPendingSendTaskSize();
					task.setAttribute(FrameworkConstants.HAVA_SET_PENDRESULT, Boolean.TRUE);
				}
			}
		};
		return new RegisteredServiceContextImpl(ch, serviceRepository, logService, currentRs, allRSListeners, streamLogOutputService, currentRs.getBundleContext());
	}
}
